﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Controls.Primitives;
using PSE.Framework.UI.Controls.SmartClient.Validation;
using System.Windows;
using System.Windows.Controls;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Data;

namespace PSE.Framework.UI.Controls.SmartClient
{
    public class ToggleButton : System.Windows.Controls.Primitives.ToggleButton, IValidatableObject
    {
        #region Enums
        public enum ToggleType
        {
            Normal,
            Navigation
        }
        #endregion

        #region Construtor
        public ToggleButton()
            : base()
        {
            //if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            //    return;

            ValidationManager.InitializeValidation(this);

            this.PropertyChanged += new PropertyChangedEventHandler(_PropertyChanged);
            this.Loaded+=new RoutedEventHandler(_Loaded);
        }

        static ToggleButton()
        {
            IsEnabledProperty.OverrideMetadata(typeof(ToggleButton), new FrameworkPropertyMetadata(OnIsEnabledChanged));
        }
        #endregion

        #region Propriedades
        
        /// <summary>
        /// Controle o tipo do Toggle Button
        /// </summary>
        public ToggleType ToggleButtonType
        {
            get { return (ToggleType)GetValue(ToggleButtonTypeProperty); }
            set { SetValue(ToggleButtonTypeProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ToggleButtonType.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ToggleButtonTypeProperty =
            DependencyProperty.Register("ToggleButtonType", typeof(ToggleType), typeof(ToggleButton), new FrameworkPropertyMetadata(ToggleType.Normal));

        /// <summary>
        /// Controla a exibição do ícone no botão
        /// </summary>
        public static readonly DependencyProperty IconProperty = DependencyProperty.Register("Icon", typeof(string), typeof(ToggleButton), new FrameworkPropertyMetadata(null, OnIconChanged));
        public string Icon
        {
            get { return (string)GetValue(IconProperty); }
            set { SetValue(IconProperty, value); }
        }
        #endregion
        

        #region IValidatableObject Members

        public static readonly DependencyProperty ClearServiceErrorsProperty = ValidationManager.ClearServiceErrorsProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Indica se o objeto dispara validação
        /// </summary>
        public bool ClearServiceErrors
        {
            get { return (bool)GetValue(ClearServiceErrorsProperty); }
            set { SetValue(ClearServiceErrorsProperty, value); }
        }

        public static readonly DependencyProperty CausesValidationProperty = ValidationManager.CausesValidationProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Indica se o objeto dispara validação
        /// </summary>
        public bool CausesValidation
        {
            get { return (bool)GetValue(CausesValidationProperty); }
            set { SetValue(CausesValidationProperty, value); }
        }

        public static readonly DependencyProperty ValidationTriggersProperty = ValidationManager.ValidationTriggersProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Triggers que disparam a validação do objeto
        /// </summary>
        public ValidationTriggerCollection ValidationTriggers
        {
            get { return (ValidationTriggerCollection)GetValue(ValidationTriggersProperty); }
        }

        public static readonly DependencyProperty ValidationRulesProperty = ValidationManager.ValidationRulesProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Regras de validação do objeto
        /// </summary>
        public ValidationRuleCollection ValidationRules
        {
            get { return (ValidationRuleCollection)GetValue(ValidationRulesProperty); }
        }

        public static readonly DependencyProperty HasErrorProperty = ValidationManager.HasErrorProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Indica se objeto tem erros
        /// </summary>
        public bool HasError
        {
            get { return (bool)GetValue(HasErrorProperty); }
        }

        public static readonly DependencyProperty ErrorsProperty = ValidationManager.ErrorsProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Erros do objeto
        /// </summary>
        public ReadOnlyObservableCollection<ValidationError> Errors
        {
            get { return (ReadOnlyObservableCollection<ValidationError>)GetValue(ErrorsProperty); }
        }

        public static readonly DependencyProperty ErrorTemplateProperty = ValidationManager.ErrorTemplateProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Template aplicado quando objeto tem erros
        /// </summary>
        public ControlTemplate ErrorTemplate
        {
            get { return (ControlTemplate)GetValue(ErrorTemplateProperty); }
            set { SetValue(ErrorTemplateProperty, value); }
        }

        /// <summary>
        /// Valida todas as properties do objeto
        /// </summary>
        /// <returns>True se todas as properties forem válidas</returns>
        public bool Validate()
        {
            return ValidationManager.Validate(this);
        }

        /// <summary>
        /// Valida a property informada do objeto
        /// </summary>
        /// <param name="property">Property a ser validada</param>
        /// <returns>True se a property for válida</returns>
        public bool Validate(DependencyProperty property)
        {
            return ValidationManager.Validate(this, property);
        }

        /// <summary>
        /// Valida todos os objetos do container pai do objeto
        /// </summary>
        /// <returns>True se todos os objetos forem válidos</returns>
        public bool ValidateParentContainer()
        {
            return ValidationManager.ValidateParentContainer(this);
        }

        #endregion

        #region IValidatable Members

        public static readonly DependencyProperty CanValidateProperty = ValidationManager.CanValidateProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Indica se o objeto suporta validação
        /// </summary>
        bool IValidatable.CanValidate
        {
            get { return (bool)GetValue(CanValidateProperty); }
        }

        public static readonly DependencyProperty ValidationGroupProperty = ValidationManager.ValidationGroupProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Grupo de validação do objeto, informar null ou string.Empty nos objetos que disparam 
        /// validação para ignorar o grupo de validação
        /// </summary>
        string IValidatable.ValidationGroup
        {
            get { return (string)GetValue(ValidationGroupProperty); }
            set { SetValue(ValidationGroupProperty, value); }
        }

        #endregion

        #region INotifyPropertyChanged Members
        public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;

        private void OnPropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
                PropertyChanged(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName));
        }

        protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
        {
            //if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(this))
            //    return;

            base.OnPropertyChanged(e);
            OnPropertyChanged(e.Property.Name);
        }
        #endregion

        #region ChangesMonitor
        private int? _sourceObjectHashCode;

        public static readonly DependencyProperty IsChangeMonitoredProperty = ChangesMonitorManager.IsChangeMonitoredProperty.AddOwner(typeof(ToggleButton));
        /// <summary>
        /// Indica se as alteracoes do objeto serao monitoradas pelo ChangesMonitorContainer
        /// </summary>
        public bool IsChangeMonitored
        {
            get { return (bool)GetValue(IsChangeMonitoredProperty); }
            set { SetValue(IsChangeMonitoredProperty, value); }
        }

        void _Loaded(object sender, RoutedEventArgs e)
        {
            ChangesMonitorContainer container = ChangesMonitorManager.GetParentChangesMonitorContainer(this);
            if (container != null)
                container.CleaningChanges += new RoutedEventHandler(container_CleaningChanges);
        }

        void container_CleaningChanges(object sender, RoutedEventArgs e)
        {
            this._sourceObjectHashCode = null;
        }

        /// <summary>
        /// Recuperar o binding para as propriedades de entrada do controle
        /// No caso do list box , as propriedades permitdas são 3
        /// SelectedValueProperty
        /// SelectedItemProperty
        /// SelectedIndexProperty
        /// </summary>
        /// <returns></returns>
        protected Binding GetBindingOfInputProperty()
        {
            return BindingOperations.GetBinding(this, ToggleButton.IsCheckedProperty);
        }

        /// <summary>
        /// Verificar se a propriedade alterada é a propriedade default do controle
        /// e disparar o evento de alteracao quando for preciso
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void _PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (this.IsChangeMonitored && e.PropertyName == ChangesMonitorManager.GetDefaultPropertyAttribute(this))
            {
                //TODO: Solucao de contorno, deverá ser revista na próxima faze
                //sempre disparar o evento para o changes monitor do grid
                RoutedEventArgs gridHasChangesEventArgs = new RoutedEventArgs(ChangesMonitorManager.GridHasChangedEvent, this);
                this.RaiseEvent(gridHasChangesEventArgs);
                bool hasChanges = true;

                Binding bind = GetBindingOfInputProperty();
                //se tiver biding associado 
                if (bind != null)
                {
                    //recuperar o hashCode do objeto source do binding
                    int? objectHashCode = this.GetHashCodeOfSourceObject(bind);

                    //se nao houve um objeto source dos dados (ou seja, se nao houver binding)
                    //disparar sempre o evento de indicacao de alteracao
                    if (objectHashCode.HasValue)
                    {
                        //se for um objeto diferente do atual, significa que houve uma alteração no bindig, sendo assim
                        //nao dever ser disparado o evento de indicacao de alteracao para o changes monitor
                        if (_sourceObjectHashCode != objectHashCode)
                        {
                            _sourceObjectHashCode = objectHashCode;
                            hasChanges = false;
                        }
                    }
                    ////se havia um objeto source e nao existe mais, limpar o hashcode e nao disparar o evento de indicacao de alteracao
                    else
                    {
                        _sourceObjectHashCode = objectHashCode;
                        hasChanges = false;
                    }
                }

                if (hasChanges)
                {
                    RoutedEventArgs controlHasChangesEventArgs = new RoutedEventArgs(ChangesMonitorManager.ControlHasChangedEvent, this);
                    this.RaiseEvent(controlHasChangesEventArgs);
                }
            }
        }
        #endregion
        
        private static void OnIconChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(d))
                return;

            ToggleButton instance = d as ToggleButton;
            if (instance != null)
            {
                if (instance.Icon != null)
                {
                    instance.updateIcon();
                }
            }
        }

        private static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (System.ComponentModel.DesignerProperties.GetIsInDesignMode(d))
                return;

            ToggleButton instance = d as ToggleButton;
            if (instance != null)
            {
                if (instance.Icon != null)
                {
                    instance.updateIcon();
                }
            }
        }

        /// <summary>
        /// Controla a exibição do ícone do togglebutton. 
        /// É responsabilidade do controle mostrar o ícone desabilitado quando o togglebutton estiver desabilitado.
        /// </summary>
        protected void updateIcon()
        {
            if (this.IsEnabled)
            {
                this.SetResourceReference(BackgroundProperty, this.Icon);
            }
            else
            {
                this.SetResourceReference(BackgroundProperty, this.Icon + "Disabled");
            }
        }
    }
}
